/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 */
import cluster from 'cluster';
import path from 'path';

import { isWindows } from '@ohos/hvigor-common';
import { FSWatcher, watch } from 'chokidar';

import { HVIGOR_PROJECT_WRAPPER_HOME } from '../../../cli/wrapper/wrapper-const.js';
import { DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME } from '../../../common/const/const.js';
import { HVIGOR_USER_HOME } from '../../../common/const/path-const.js';
import { HVIGOR_PROCESS_EVENT_ID } from '../../internal/lifecycle/event/event-id-options.js';
import { hvigorProcess } from '../../internal/lifecycle/hvigor-process.js';
import { HvigorConfigReader } from '../../util/hvigor-config-reader.js';
import { HvigorDaemonLogger } from '../log/hvigor-daemon-log.js';

const _log = HvigorDaemonLogger.getLogger('daemon');
let watcher: FSWatcher;
let isRunning = false;
const hvigorFiles: string[] = [];
let hvigorConfigWatcher: FSWatcher;
if (cluster.isWorker) {
  // 在worker进程里监听hvigorfile所依赖的全部模块，发生变化就主动触发重启
  // 由于一个很可能是node的问题，在windows上监听文件时会存在占用文件、导致重命名或移动目录失败的问题：
  // https://github.com/paulmillr/chokidar/issues/664
  // 因此在windows上使用轮询方式规避
  watcher = watch([], {
    usePolling: isWindows(),
    interval: 1000, // 单位ms 在usePolling为false时字段不使能
  });
  watcher.on('change', (path) => {
    const disconnect = () => {
      _log.debug(`worker ${cluster.worker!.id} detected hvigorfile's dependency changed. Path ${path} changed. Will exit and start another worker process.`);
      if (cluster.worker!.isConnected()) {
        _log.debug('worker isConnected start disconnect');
        cluster.worker!.disconnect();
        cluster.worker!.kill();
      }
    };
    if (isRunning) {
      hvigorProcess.once(HVIGOR_PROCESS_EVENT_ID.FINISHED, disconnect);
      hvigorProcess.once(HVIGOR_PROCESS_EVENT_ID.FAILED, disconnect);
    } else {
      disconnect();
    }
  });

  hvigorConfigWatcher = watch([path.resolve(HVIGOR_PROJECT_WRAPPER_HOME, DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME)], {
    usePolling: isWindows(),
    interval: 1000, // 单位ms 在usePolling为false时字段不使能
  });
  hvigorConfigWatcher.on('change', (path) => {
    if (isRunning) {
      hvigorProcess.once(HVIGOR_PROCESS_EVENT_ID.FINISHED, disconnectHvigorConfig);
      hvigorProcess.once(HVIGOR_PROCESS_EVENT_ID.FAILED, disconnectHvigorConfig);
    } else {
      disconnectHvigorConfig();
    }
  });
}

function disconnectHvigorConfig() {
  const execArgv = process.execArgv;

  // 寻找--max-old-space-size 参数的索引
  const maxOldSpaceIndex = execArgv.findIndex((arg) => arg.startsWith('--max-old-space-size='));
  const execArgvMaxOldSpaceSize = Number(execArgv[maxOldSpaceIndex].split('=')[1]);
  const newSpaceSize = HvigorConfigReader.getMaxOldSpaceSize(true);
  const oldSpaceSizeChanged = execArgvMaxOldSpaceSize !== newSpaceSize;

  // 寻找--max-semi-space-size 参数的索引
  const maxSemiSpaceIndex = execArgv.findIndex((arg) => arg.startsWith('--max-semi-space-size='));
  const execArgvMaxSemiSpaceSize = Number(execArgv[maxSemiSpaceIndex].split('=')[1]);
  const newSemiSpaceSize = HvigorConfigReader.getMaxSemiSpaceSize(true);
  const semiSpaceSizeChanged = execArgvMaxSemiSpaceSize !== newSemiSpaceSize;

  const exposeGCIndex = execArgv.findIndex((arg) => arg.startsWith('--expose-gc'));
  const exposeGC = HvigorConfigReader.getHvigorConfig()?.nodeOptions?.exposeGC;
  const exposeGCChanged = (exposeGCIndex === -1 && exposeGC !== false) || (exposeGCIndex !== -1 && exposeGC === false);

  if (oldSpaceSizeChanged || semiSpaceSizeChanged || exposeGCChanged) {
    _log.debug(`Worker ${cluster.worker!.id} detected changes in 'hvigor-config.json5' nodeOptions.maxOldSpaceSize or maxSemiSpaceSize
        or exposeGC. Path ${path} changed. Will exit and start another worker process.`);
    if (cluster.worker!.isConnected()) {
      _log.debug('worker isConnected start disconnect');
      cluster.worker!.disconnect();
      cluster.worker!.kill();
    }
  }
}

export function addConfigDeps() {
  const files = getModuleDependencies(hvigorFiles).filter((value) => {
    const relative = path.relative(HVIGOR_USER_HOME, value);
    return !(relative && !relative.startsWith('..') && path.isAbsolute(relative) && !(value.includes('hvigorfile.ts') || value.includes('hvigorconfig.ts')));
  });
  if (cluster.isWorker) {
    _log.debug('watch files:', files);
    watcher.add(files);
  }
}

export function addModuleDependency(modulePath: string) {
  if (!hvigorFiles.includes(modulePath)) {
    hvigorFiles.push(modulePath);
  }
}

export function setRunning(running: boolean) {
  isRunning = running;
}

/**
 * 解析一个node模块的所有依赖模块
 *
 * @param {string} modulePaths
 * @returns {string[]}
 */
function getModuleDependencies(modulePaths: string[]): string[] {
  const dependencies: string[] = modulePaths.map((modulePath) => require.resolve(modulePath));

  modulePaths.forEach((modulePath) => {
    const resolvedPath = require.resolve(modulePath);
    const moduleCache = require.cache[resolvedPath];

    function traverseDependencies(module: NodeModule) {
      module.children.forEach((child) => {
        if (!dependencies.includes(child.filename)) {
          dependencies.push(child.filename);
          traverseDependencies(child);
        }
      });
    }

    if (moduleCache !== undefined) {
      traverseDependencies(moduleCache);
    }
  });

  return dependencies;
}
